/*
* usbddemo.c                                                Version 2.57
*
* smxUSBD Standalone Demo.
*
* Copyright (c) 2005-2016 Micro Digital Inc.
* All rights reserved. www.smxrtos.com
*
* This software is confidential and proprietary to Micro Digital Inc.
* It has been furnished under a license and may be used, copied, or
* disclosed only in accordance with the terms of that license and with
* the inclusion of this header. No title to nor ownership of this
* software is hereby transferred.
*
* Author: Yingbo Hu
*
* Portable to any ANSI compliant C compiler.
*
*****************************************************************************/
#include <aos/kernel.h>
#include <ulog/ulog.h>
#define SMXUSBD
#include "usbddemo.h"
#if defined(SMXUSBD)
#include "smxbase.h"
#include "smxusbd.h"
#include "boscsky.h"
#include "mm_config.h"
#include "udvideo.h"
#include "udintern.h"

#if defined(SB_CPU_MCF5445x)
#include "cfdefs.h"
#endif
#include "imageusbtask/usbdtsm.h"
#include "image_task.h"
#include "image_proc.h"
#include "drv_isp.h"
#include "drv_dmac.h"
#include "vpu_to_jpeg.h"


#define DEMO_AUDIO   SUD_AUDIO
#define DEMO_DFU     SUD_DFU
#define DEMO_KBD     SUD_KBD
#define DEMO_MOUSE   SUD_MOUSE
#define DEMO_MTP     SUD_MTP
#define DEMO_HIDCOM  SUD_HIDCOM
#define DEMO_SERIAL  SUD_SERIAL
#define DEMO_MSTOR   SUD_MSTOR
#define DEMO_VIDEO   SUD_VIDEO
#define DEMO_FTEMPL  SUD_FTEMPL 
//#define DEMO_MSTOR_RAMDISK 0 /* if 1 use local RAM disk, else use smxFS driver */
#define DEMO_MSTOR_RAMDISK 1 /* if 1 use local RAM disk, else use smxFS driver */

/* Enable ONE */
#define MOUSE_CIRCLE      0  /* requires sin and cos and floating point */
//#define MOUSE_DIAMOND     1
#define MOUSE_DIAMOND     0
#define MOUSE_STRESS_TEST 0


#if (DEMO_MSTOR && !DEMO_MSTOR_RAMDISK) || DEMO_MTP
#include "smxfs.h"
#if (DEMO_MTP && SFS_DRV_RAMDISK)
#include "image.h"
#endif
#endif

struct _IMAGE_SOURCE    g_str_image_source;

extern uint32_t *g_fb_ir_cur;
extern uint32_t *g_fb_rgb_cur ;
extern uint32_t g_usb_image_type;
extern  uint32_t *g_fb_ir_cur;
extern int g_jpeg_commpre_begin;
extern uint8_t *iva_2ir_get_buffer(uint32_t , uint32_t );

/* Implemented in main.c */
extern void UartPutString(const char *pInfo);


#if DEMO_SERIAL
static int g_iPort;
static int g_iNotification = -1;
static unsigned char *g_pBuf = NULL;
static void USBDSerialNotification(int port, int notification);
static void USBDSerialNotificationDo(int port, int notification);
#endif

#if DEMO_MOUSE
static void send_mouse_event(void);
#endif

#if DEMO_AUDIO
static void send_audio_data(void);
#if SUD_AUDIO_MIDI_NUMBER
static void send_midi_data(void);
#endif
static void USBDAudioNotification(int port, int notification, int parameter);
#endif

#if DEMO_MSTOR && DEMO_MSTOR_RAMDISK

#define RAMDISK_SECTOR 0x200

#define FAT_12                  12
#define FAT_16                  16

/* Set RAM disk size */
#define RAMDISK_SIZE0    0x100000  /* 1MB */
static u8 g_pRAMBuf[RAMDISK_SIZE0];
static const SB_BD_IF *GetRAM0DiskInterface(void);
static int Format1216(unsigned char *pRAMDisk, uint iFatType, u32 dwTotalSectors);
#endif

#if DEMO_DFU
static int g_iDFUWrite = 0;
static void DFUDo(void);
static int DemoDFUInit(uint segment);
static int DemoDFUWrite(uint segment, u32 dwOffset, u8 *pData, uint iSize);
static int DemoDFURead(uint segment, u32 dwOffset, u8 *pData, uint iSize);
static int DemoDFUDone(uint segment);
static SUD_DFU_IF DFUIF = {
    DemoDFUInit,
    DemoDFUWrite,
    DemoDFURead,
    DemoDFUDone,
};
#endif

#if DEMO_HIDCOM
static int g_iHIDReady = 0;
static void HIDComDo(void);
static void HIDOutputNotify(uint iReportID, void *pDataBuf, uint size);
#endif

#if DEMO_KBD
static void send_kbd_event(void);
#endif

#if DEMO_MTP
static int SMXMTPDiskMounted(uint index);
static int SMXMTPStorageNum(void);
static void *SMXMTPOpen(uint index, char *pFileName, uint iMode);
static int SMXMTPClose(void *pFileHandle);
static int SMXMTPRead(u8 *pRAMAddr, u32 dwSize, void *pFileHandle);
static int SMXMTPWrite(u8 *pRAMAddr, u32 dwSize, void *pFileHandle);
static int SMXMTPMkDir(uint index, char *pPathName);
static int SMXMTPRmDir(uint index, char *pPathName);
static int SMXMTPDelete(uint index, char *pFileName);
static int SMXMTPTotalSize(uint index);
static int SMXMTPFreeSize(uint index);
static int SMXMTPFindFirst(uint index, char *pFindSpec, SUD_MTP_FILE_INFO *pFileInfo);
static int SMXMTPFindNext(uint iID, SUD_MTP_FILE_INFO *pFileInfo);
static int SMXMTPFindClose(SUD_MTP_FILE_INFO *pFileInfo);
static int SMXMTPGetImgProp(uint index, char *pFileName, uint *piWidth, uint *piHeight, uint *piBits, uint *piThumbSize, uint *piThumbFormat, uint *piThumbWidth, uint *piThumbHeight);
static void *SMXMTPOpenImgThumb(uint index, char *pFileName);
static int SMXMTPReadImgThumb(u8 *pRAMAddr, u32 dwSize, void *pHandle);
static int SMXMTPCloseImgThumb(void *pHandle);
static int SMXMTPGetFormat(uint index, char *pFileName, u16 *pwFormat);
static int SMXMTPInitCapture(uint index, uint format);
static int SMXMTPStartCapture(void);
static int SMXMTPStopCapture(void);
const static SUD_MTP_IF SMXMTPInterface = {
    SMXMTPDiskMounted,
    SMXMTPStorageNum,
    SMXMTPOpen,
    SMXMTPClose,
    SMXMTPRead,
    SMXMTPWrite,
    SMXMTPMkDir,
    SMXMTPRmDir,
    SMXMTPDelete,
    SMXMTPTotalSize,
    SMXMTPFreeSize,
    SMXMTPFindFirst,
    SMXMTPFindNext,
    SMXMTPFindClose,
    SMXMTPGetImgProp,
    SMXMTPOpenImgThumb,
    SMXMTPReadImgThumb,
    SMXMTPCloseImgThumb,
    SMXMTPGetFormat,
    SMXMTPInitCapture,
    SMXMTPStartCapture,
    SMXMTPStopCapture,
};
#endif

#if DEMO_VIDEO
STATIC int     usbd_video_recording;
void    VideoDo(void);
static void    USBDVideoNotification(int port, int notification, u32 parameter);
#endif

static void USBDPutString(char *pInfo);
extern int g_jpeg_Size;
int g_usb_send_flag = 0;
extern  int g_jpeg_commpre_begin;

uint32_t  VPU_OUT_UVC_BASE = MM_IMG_ISP1_BUF0;
//char jpg_out_addr[640*480] ={0};
char *jpg_out_addr = NULL;


extern VpuPrivate_t VpuPrivate;


int USBDDemo(void)
{
    //uint i,j;
    //i = SUD_SYNOPSYS_USE_SLAVEMODE;
    //j = SUD_MAX_ENDPOINTS;
#if defined(SB_CPU_MCF5445x)
    if (0 == (MCF_USB_OTG_OTGSC & MCF_USB_OTG_OTGSC_ID)) {
        USBDPutString("Need switch to host mode");
        return -1;
    }

#endif
#if DEMO_MOUSE
    send_mouse_event();
    sb_OS_WAIT_MSEC_POLL(16);
#endif
#if DEMO_SERIAL

    if (g_iNotification != -1) {
        USBDSerialNotificationDo(g_iPort, g_iNotification);
        g_iNotification = -1;
    }

#endif
#if DEMO_AUDIO
#if (SUD_AUDIO_INCLUDE_MIC + SUD_AUDIO_INCLUDE_SPK) > 0
    send_audio_data();
#endif
#if SUD_AUDIO_MIDI_NUMBER
    //send_midi_data();
#endif
#endif
#if DEMO_MSTOR
    //TimerMDelay(20);
#endif
#if DEMO_DFU
    DFUDo();
#endif
#if DEMO_HIDCOM
    HIDComDo();
#endif
#if DEMO_KBD
    send_kbd_event();
#endif
#if DEMO_VIDEO
    //VideoDo();
#endif
    return 0;
}


int USBDDemoStart(void)
{
    int result = -1;
#if defined(SB_CPU_MCF5445x)

    if (0 == MCF_USB_OTG_OTGSC & MCF_USB_OTG_OTGSC_ID) {
        return -1;
    }

#endif

    if (sud_Initialize(SUD_ALL_MASK)) {
        USBDPutString("smxUSBD Demo Started");
#if DEMO_AUDIO
        sud_AudioRegisterNotify(0, USBDAudioNotification);
#endif
#if DEMO_SERIAL
        //g_pBuf = (unsigned char *)malloc(SUD_SERIAL_MTU);
        g_pBuf = (unsigned char *)sb_OS_MEM_ALLOC(SUD_SERIAL_MTU);
        sud_SerialRegisterPortNotify(0, USBDSerialNotification);
#endif
#if DEMO_MSTOR
#if DEMO_MSTOR_RAMDISK
       // sud_MSRegisterDisk(GetRAM0DiskInterface(), 0);
        /* Format the RAM disk only once so that we can unplug and
           re-plug the USB cable, and the data will be kept. This
           prevents Windows from reading the data from its cache
           so we can test the reading function. Otherwise Windows
           will not read data from the mass storage device.
        */
        //memset(g_pRAMBuf, 0xFF, RAMDISK_SIZE0);
        //Format1216(g_pRAMBuf, FAT_12, RAMDISK_SIZE0 / RAMDISK_SECTOR);
#else
        sud_MSRegisterDisk(sfs_GetMMCSD0Interface(), 0);
#endif
#endif
#if DEMO_DFU
        sud_DFURegisterInterface(&DFUIF);
#endif
#if DEMO_HIDCOM
        sud_HIDRegisterOutputNotify(HIDOutputNotify);
#endif
#if DEMO_MTP

        if (sfs_init() == SB_FAIL) {
            USBDPutString("sfs_init() failed.");
        } else {
            int iID = 0;
            /* Register built-in device drivers. */
#if SFS_DRV_RAMDISK
            sfs_devreg(sfs_GetRAM0Interface(), iID++);
            //sfs_devreg(sfs_GetRAM1Interface(), iID++);
            u32 iTotalSize = sizeof(imageData);
            FILEHANDLE fHd = sfs_fopen("a:\\logo.bmp", "wb");
            u32 written = sfs_fwrite((void *)imageData, 1, iTotalSize, fHd);

            if (written < iTotalSize) {
                USBDPutString("write image failed.");
            }

            sfs_fclose(fHd);
#endif
#if SFS_DRV_USB
            sfs_devreg(sfs_GetUSB0Interface(), iID++);
            //sfs_devreg(sfs_GetUSB1Interface(), iID++);
#endif
#if SFS_DRV_MMCSD
            sfs_devreg(sfs_GetMMCSD0Interface(), iID++);
#endif
#if SFS_DRV_NANDFLASH
            sfs_devreg(sfs_GetNAND0Interface(), iID++);
            //sfs_devreg(sfs_GetNAND1Interface(), iID++);
#endif
#if SFS_DRV_NORFLASH
            sfs_devreg(sfs_GetNOR0Interface(), iID++);
            //sfs_devreg(sfs_GetNOR1Interface(), iID++);
#endif
#if SFS_DRV_ATA
            sfs_devreg(sfs_GetATA0Interface(), iID++);
            //sfs_devreg(sfs_GetATA1Interface(), iID++);
#endif
#if SFS_DRV_CF
            sfs_devreg(sfs_GetCFInterface(), iID++);
#endif
#if SFS_DRV_DOC
            sfs_devreg(sfs_GetDOC0Interface(), iID++);
            //sfs_devreg(sfs_GetDOC1Interface(), iID++);
#endif
        }

        sud_MTPRegisterInterface(&SMXMTPInterface);
#endif

#if DEMO_VIDEO
        usbd_video_recording = 0;
        sud_VideoRegisterNotify(0, USBDVideoNotification);
#endif
#if DEMO_FTEMPL
		/* register callback function for received INT and BULK data */
		sud_FTemplRegisterNotify(0, ftempl_func);
#endif
        result = 0;
    } else {
        USBDPutString("smxUSBD initialization failed");
    }

    return result;
}

int USBDDemoStop(void)
{
    if (sud_USBInited()) {
#if DEMO_SERIAL
        free(g_pBuf);
#endif
#if DEMO_MSTOR
        sud_MSRegisterDisk(NULL, 0);
#endif
#if DEMO_MTP
        sfs_exit();
#endif

        sud_Release();
        USBDPutString("smxUSBD demo exit");
    }

    return 0;
}

/*==========================================================================*/

static void USBDPutString(char *pInfo)
{
    /* csky test */
    printf("%s\n", pInfo);
    //UartPutString(pInfo);
    //UartPutString("\r\n");
}

#if DEMO_MOUSE
static void send_mouse_event(void)
{
    static int i = 0;
    char pTempBuf[64];
    char pTempBuf1[32];
    u8 button = 0;
    s8 x = 0;
    s8 y = 0;
    s8 wheel = 0;
#if MOUSE_CIRCLE
    int oldx = 100, oldy = 0;
    int degree = 0;
#endif

#if MOUSE_STRESS_TEST
    /* move the mouse cursor randomly as fast as possible on the host screen */
    wheel = ((rand() % 2) ? SUD_MOUSE_WHEEL_UP : SUD_MOUSE_WHEEL_DOWN);
    x = 100 * ((rand() % 2) ? 1 : -1);
    y = 100 * ((rand() % 2) ? 1 : -1);

    if (sud_MouseInput(button, x, y, wheel)) {
        _ultoa(i++, pTempBuf1, 10);
        strcpy(pTempBuf, "Sending Mouse Event ");
        strcat(pTempBuf, pTempBuf1);
        USBDPutString(pTempBuf);
    }

    sb_DelayUsec(50 * 1000L);
#elif MOUSE_CIRCLE
    degree += 10;
    degree = degree % 360;
    x = (int)(100.0f * cosf(degree * DtoR));
    y = (int)(100.0f * sinf(degree * DtoR));

    if (sud_MouseInput(button, x - oldx, y - oldy, wheel)) {
        _ultoa(i++, pTempBuf1, 10);
        strcpy(pTempBuf, "Sending Mouse Event ");
        strcat(pTempBuf, pTempBuf1);
        USBDPutString(pTempBuf);
    }

    oldx = x;
    oldy = y;
    sb_DelayUsec(1000 * 1000L);

#elif MOUSE_DIAMOND
#define DIA_INCR  2
#define DIA_SZ    100  /* diamond width (or height) in pixels */
#define DIA_STEPS ((DIA_SZ)/(DIA_INCR))

    if ((i / DIA_STEPS) % 2 == 0) {
        x = DIA_INCR;
    } else {
        x = 0 - DIA_INCR;
    }

    if (((i + DIA_STEPS / 2) / DIA_STEPS) % 2 == 0) {
        y = 0 - DIA_INCR;
    } else {
        y = DIA_INCR;
    }

    if (sud_MouseInput(button, x, y, wheel)) {
        _ultoa(i++, pTempBuf1, 10);
        strcpy(pTempBuf, "Sending Mouse Event ");
        strcat(pTempBuf, pTempBuf1);
        USBDPutString(pTempBuf);

        /* Do long delay every time we trace diamond so mouse can be used. */
        if (i % (2 * DIA_STEPS) == 0) {
            sb_DelayMsec(3000);
        } else {
            sb_DelayMsec(10);
        }
    }

#else
#error Select Mouse movement setting at top of demo.c.
#endif

}
#endif

#if DEMO_SERIAL
static unsigned long g_iTotalReceived = 0;

static void USBDSerialShowLineState(int port)
{
    u32 iState;
    sud_SerialGetLineState(port, (uint *)&iState);
    char pTempBuf[32] = "Line State:";

    if (iState & SUD_CDC_LINE_OUT_DTR) {
        strcat(pTempBuf, "DTR ");
    }

    if (iState & SUD_CDC_LINE_OUT_RTS) {
        strcat(pTempBuf, "RTS ");
    }

    USBDPutString(pTempBuf);
}

static void USBDSerialShowLineCoding(int port)
{
    u32 dwDTERate;
    u8 bStopBits;
    u8 bParityType;
    u8 bDataBits;
    char pTempBuf[32] = "";
    char pTempBuf1[8];

    if (0 == sud_SerialGetLineCoding(port, &dwDTERate, &bParityType, &bDataBits, &bStopBits)) {
        return;
    }

    _ultoa(dwDTERate, pTempBuf1, 10);
    strcat(pTempBuf, pTempBuf1);
    strcat(pTempBuf, " ");

    switch (bParityType) {
        case SUD_CDC_PARITY_NONE:
            strcat(pTempBuf, "N ");
            break;

        case SUD_CDC_PARITY_ODD:
            strcat(pTempBuf, "O ");
            break;

        case SUD_CDC_PARITY_EVEN:
            strcat(pTempBuf, "E ");
            break;

        case SUD_CDC_PARITY_MARK:
            strcat(pTempBuf, "M ");
            break;

        case SUD_CDC_PARITY_SPACE:
            strcat(pTempBuf, "S ");
            break;
    }

    _ultoa(bDataBits, pTempBuf1, 10);
    strcat(pTempBuf, pTempBuf1);
    strcat(pTempBuf, " ");

    switch (bParityType) {
        case SUD_CDC_STOP_BITS_1:
            strcat(pTempBuf, "1");
            break;

        case SUD_CDC_STOP_BITS_1_5:
            strcat(pTempBuf, "1.5");
            break;

        case SUD_CDC_STOP_BITS_2:
            strcat(pTempBuf, "2");
            break;
    }

    USBDPutString(pTempBuf);
}

static void USBDSerialNotificationDo(int port, int notification)
{
    int len;
    char pTempBuf[64];
    char pTempBuf1[16];

    switch (notification) {
        case SUD_CDC_NOTIFY_DATA_READY:
            len = sud_SerialDataLen(port);

            if (len > 0 && g_pBuf) {
                if (len > SUD_SERIAL_MTU) {
                    len = SUD_SERIAL_MTU;
                }

                len = sud_SerialReadData(port, g_pBuf, len);
                g_iTotalReceived += len;
                _ultoa(g_iTotalReceived, pTempBuf1, 10);
                strcpy(pTempBuf, "Gotten Data ");
                strcat(pTempBuf, pTempBuf1);
                USBDPutString(pTempBuf);
                sud_SerialWriteData(port, g_pBuf, len);
            }

            break;

        case SUD_CDC_NOTIFY_LINE_STATE_CHANGE:
            USBDSerialShowLineState(port);
            break;

        case SUD_CDC_NOTIFY_SET_LINE_CODING:
            USBDSerialShowLineCoding(port);
            break;
    }
}

static void USBDSerialNotification(int port, int notification)
{
    g_iNotification = notification;
    g_iPort = port;
}

#endif



#if DEMO_MSTOR && DEMO_MSTOR_RAMDISK


#define MBR_SECTOR  0

static unsigned long g_iReadBytes = 0;
static unsigned long g_iWriteBytes = 0;
static unsigned long g_iLastReadBytes = 0;
static unsigned long g_iLastWriteBytes = 0;

static void USBDStorageInfo(void)
{
    /*
        char pTempBuf[64] = "R/W: ";
        char pTempBuf1[16];
        _ultoa(g_iReadBytes, pTempBuf1, 10);
        strcat(pTempBuf, pTempBuf1);
        strcat(pTempBuf, "/");
        _ultoa(g_iWriteBytes, pTempBuf1, 10);
        strcat(pTempBuf, pTempBuf1);
        USBDPutString(pTempBuf);
    */
}

#if defined(__IAR_SYSTEMS_ICC__)
/* __no_init keyword puts the data in uninit data (DATA_N) section not in
   init data (DATA_I) or init data image in flash (DATA_ID). */
#define NO_INIT __no_init
#else
#define NO_INIT
#endif


static int RAMDriverInit(void)
{
    return SB_PASS;
}

static int RAMDriverRelease(void)
{
    return SB_PASS;
}

static int RAMDiskOpen(void)
{
    return SB_PASS;
}

static int RAMDiskClose(void)
{
    return SB_PASS;
}

static int RAMSectorRead(u8 *pRAMAddr, u32 dwStartSector, u16 wHowManySectors)
{
    u8 *pRAMBuf = g_pRAMBuf;
#if defined(SB_CPU_16BIT)
    pRAMBuf += (uint)(dwStartSector * RAMDISK_SECTOR);

    while (wHowManySectors) {
        memcpy(pRAMAddr, pRAMBuf, RAMDISK_SECTOR);
        pRAMBuf += RAMDISK_SECTOR;
        pRAMAddr += RAMDISK_SECTOR;
        wHowManySectors--;
    }

#else
    memcpy(pRAMAddr, pRAMBuf + dwStartSector * RAMDISK_SECTOR, wHowManySectors * RAMDISK_SECTOR);
#endif
    g_iReadBytes += wHowManySectors * 512;

    if (g_iReadBytes - g_iLastReadBytes > 50 * 1024 || g_iReadBytes < 5 * 1024) {
        g_iLastReadBytes = g_iReadBytes;
        USBDStorageInfo();

    }

    return 0;
}

static int RAMSectorWrite(u8 *pRAMAddr, u32 dwStartSector, u16 wHowManySectors)
{
    u8 *pRAMBuf = g_pRAMBuf;
#if defined(SB_CPU_16BIT)
    pRAMBuf += (uint)(dwStartSector * RAMDISK_SECTOR);

    while (wHowManySectors) {
        memcpy(pRAMBuf, pRAMAddr, RAMDISK_SECTOR);
        pRAMBuf += RAMDISK_SECTOR;
        pRAMAddr += RAMDISK_SECTOR;
        wHowManySectors--;
    }

#else
    memcpy(pRAMBuf + dwStartSector * RAMDISK_SECTOR, pRAMAddr, wHowManySectors * RAMDISK_SECTOR);
#endif
    g_iWriteBytes += wHowManySectors * 512;

    if (g_iWriteBytes - g_iLastWriteBytes > 50 * 1024 || g_iWriteBytes < 5 * 1024) {
        g_iLastWriteBytes = g_iWriteBytes;
        USBDStorageInfo();

    }

    return 0;
}

static int RAMIOCtl(uint dwCommand, void *pParameter)
{
    int result = SB_PASS;

    switch (dwCommand) {
        case SB_BD_IOCTL_INSERTED:
            *((int *)pParameter) = SB_PASS;
            break;

        case SB_BD_IOCTL_REMOVED:
            *((int *)pParameter) = SB_FAIL;
            break;

        case SB_BD_IOCTL_CHANGED:
            *((int *)pParameter) = SB_FAIL;
            break;

        case SB_BD_IOCTL_WRITEPROTECT:
            *((int *)pParameter) = SB_FAIL;
            break;

        case SB_BD_IOCTL_GETDEVINFO: {
            SB_BD_DEVINFO *pDeviceInfo = (SB_BD_DEVINFO *)pParameter;
            pDeviceInfo->dwSectorsNum = RAMDISK_SIZE0 / RAMDISK_SECTOR;
            pDeviceInfo->dwSectorSize = RAMDISK_SECTOR;
            pDeviceInfo->wPartition = 0;
            pDeviceInfo->wAutoFormat = 1;
            pDeviceInfo->wFATNum = 1;
            pDeviceInfo->wRootDirNum = 256;
            pDeviceInfo->wRemovable = 0;
            break;
        }

        case SB_BD_IOCTL_FLUSH:
            break;
    }

    return result;
}


const static SB_BD_IF RAM0Interface = {
    RAMDriverInit,
    RAMDriverRelease,
    RAMDiskOpen,
    RAMDiskClose,
    RAMSectorRead,
    RAMSectorWrite,
    RAMIOCtl
};

static const SB_BD_IF *GetRAM0DiskInterface(void)
{
    return &RAM0Interface;
}
/* The following code is used to automatically format the RAM disk. */

#if __packed_pragma
#pragma pack(1)  /* pack structures (byte alignment) */
#endif

/* BPB for FAT12/16 */
typedef __packed struct {
    u8   JmpBoot[3];
    char OEMName[8];
    u16  BytesPerSec;
    u8   SecPerClu;
    u16  RsvdSecCnt;
    u8   NumFATs;
    u16  RootEntCnt;
    u16  TotSec16;
    u8   Media;
    u16  FATSz16;
    u16  SecPerTrk;
    u16  NumHeads;
    u32  HiddSec;
    u32  TotSec32;

    u8   DrvNum;
    u8   Reserved1;
    u8   BootSig;
    u32  VolID;
    u8   VolLab[11];
    u8   FilSysType[8];
} __packed_gnu USBDBPB16;

#if __packed_pragma
#pragma pack()
#endif

/* Disk size and SectorsPerClustor mapping table */
typedef __packed struct {
    u32  DiskSize;           /* in sectors , DiskSize*BytesPerSector = MediaSize (in bytes ) */
    u8   SecPerClusVal;      /* sector per cluster */
} __packed_gnu SECPERCLUSTABLE;


/* FAT NODE const structure */
typedef struct {
    u32  FAT0;
    u32  FAT1;
    u32  FAT_NODE_FREE;
    u32  FAT_NODE_EOC;
    u32  FAT_NODE_BAD;
    char *FILE_SYSTEM_TYPE;
} USBDFATCONSDEF;

#define OEM_NAME                "MSDOS5.0"
#define OEM_NAME_LEN            8
/* Extended Boot Signature and Volume */
#define BOOT_SIGNATURE          0x29
#define VOLUME_ID               0x12345678L
#define VOLUME_LABEL_LEN        11                  /* do not change it */
#define NO_VOLUME_LABEL         "NO NAME    "       /* do not exceed VOLUME_LABEL_LEN */

/* Sector, Head and Track, used for format */
#define SECTOR_PER_TRACK        0x20
#define NUMBER_OF_HEADS         0x04
#define HIDDEN_SECTORS          0x00
/* Int13 Driver Number */
#define DRIVER_NUMBER           0x80

/* Used for Window NT */
#define RESERVED1               0x00

/* Active Partition Table Flag */
#define ACTIVE_PARTITION_FLAG   0x80

#define FS_TYPE_STR_LEN         8

STATIC USBDFATCONSDEF  FAT12DEF = {0x0FF8L, 0x0FFFL, 0x0000L, 0x0FFFL, 0x0FF7L, "FAT12   " };

STATIC USBDFATCONSDEF  FAT16DEF = {0xFFF8L, 0xC000L, 0x0000L, 0xFFFFL, 0xFFF7L, "FAT16   " };

/* FAT12 mapping table */
#define MAXARR_SECPERCLUS_12     2
STATIC SECPERCLUSTABLE  SecPerClusArr12[MAXARR_SECPERCLUS_12] = {
    {     16384L,  2},   /* less than 8M */
    {0xFFFFFFFFL,  0}    /* more than 8M */
};

/* FAT16 mapping table */
#define MAXARR_SECPERCLUS_16     8
STATIC SECPERCLUSTABLE  SecPerClusArr16[MAXARR_SECPERCLUS_16] = {
    {      8400L,  0},     /* less than 4.1M */
    {     32680L,  2},     /* 16M */
    {    262144L,  4},     /* 128M */
    {    524288L,  8},     /* 256M */
    {   1048576L, 16},     /* 512M */
    {   2097152L, 32},     /* 1G */
    {   4194304L, 64},     /* 2G */
    {0xFFFFFFFFL,  0}      /* more than 2G */
};

#if SB_CPU_BIG_ENDIAN

STATIC void InvertFATBPB16(USBDBPB16 *pBPB)
{
    pBPB->BytesPerSec = SUD_INVERT_U16(pBPB->BytesPerSec);
    pBPB->RsvdSecCnt = SUD_INVERT_U16(pBPB->RsvdSecCnt);
    pBPB->RootEntCnt = SUD_INVERT_U16(pBPB->RootEntCnt);
    pBPB->TotSec16 = SUD_INVERT_U16(pBPB->TotSec16);
    pBPB->FATSz16 = SUD_INVERT_U16(pBPB->FATSz16);
    pBPB->SecPerTrk = SUD_INVERT_U16(pBPB->SecPerTrk);
    pBPB->NumHeads = SUD_INVERT_U16(pBPB->NumHeads);
    pBPB->HiddSec = SUD_INVERT_U32(pBPB->HiddSec);
    pBPB->TotSec32 = SUD_INVERT_U32(pBPB->TotSec32);
    pBPB->VolID = SUD_INVERT_U32(pBPB->VolID);
}
#else
#define    InvertFATBPB16(pBPB)
#endif

STATIC u32 CalFATSize(u32 dwTotalSectors, u8 bSecPerClus, uint iFatType,
                      uint dwBytesPerSec, uint wRootDirNum, uint wFATNum)
{
    u32  RootDirSectors, TmpVal1, TmpVal2, FATSz;
    (void)iFatType;

    TmpVal2 = (256 * bSecPerClus) + wFATNum;
    RootDirSectors = (wRootDirNum * 32 + dwBytesPerSec - 1) / dwBytesPerSec;
    TmpVal1 = dwTotalSectors - (1 + RootDirSectors);
    FATSz = (TmpVal1 + TmpVal2 - 1) / TmpVal2;

    return FATSz;
}

STATIC u8 GetSecPerClus(u32 dwTotalSectors, uint iFatType)
{
    uint            iMaxArr;
    SECPERCLUSTABLE  *pArr;
    u8             bSecPerClus = 2;

    if (iFatType == FAT_12) {
        iMaxArr = MAXARR_SECPERCLUS_12;
        pArr = SecPerClusArr12;
    } else if (iFatType == FAT_16) {
        iMaxArr = MAXARR_SECPERCLUS_16;
        pArr = SecPerClusArr16;
    }

    /* search in the mapping table */
    for (; iMaxArr > 0 ; iMaxArr--, pArr++) {
        if (dwTotalSectors <= (*pArr).DiskSize) {
            bSecPerClus = (*pArr).SecPerClusVal;
            break;
        }
    }

    return bSecPerClus;
}

STATIC int Format1216(unsigned char *pRAMDisk, uint iFatType, u32 dwTotalSectors)
{
    uint     j;
    u8       *pMem;
    u16      FATSz16;
    u16      wTemp;
    USBDBPB16   *pSBD_BPB16;
    u32      dwRootDirSector;
    USBDFATCONSDEF  *pFD = NULL;

    if (NULL == (pMem = (u8 *)malloc(512))) {
        return  0;
    }

    /* fill the BPB structure */
    memset(pMem, 0x00, 512);
    pSBD_BPB16 = (USBDBPB16 *)pMem;
    pSBD_BPB16->JmpBoot[0] = 0xEB;
    pSBD_BPB16->JmpBoot[1] = 0x3C;
    pSBD_BPB16->JmpBoot[2] = 0x90;
    memcpy(pSBD_BPB16->OEMName, OEM_NAME, OEM_NAME_LEN);
    pSBD_BPB16->BytesPerSec = 512;
    pSBD_BPB16->SecPerClu  = GetSecPerClus(dwTotalSectors, iFatType);
    pSBD_BPB16->RsvdSecCnt = 1;
    pSBD_BPB16->NumFATs    = 2;
    pSBD_BPB16->RootEntCnt = 512;

    pSBD_BPB16->TotSec16 = (u16)(dwTotalSectors & 0xFFFF);

    pSBD_BPB16->Media = 0xF8;
    FATSz16 = (u16)(CalFATSize(dwTotalSectors, pSBD_BPB16->SecPerClu, iFatType, 512, 512, 2) & 0xFFFF);
    pSBD_BPB16->FATSz16   = FATSz16;
    pSBD_BPB16->SecPerTrk = SECTOR_PER_TRACK;
    pSBD_BPB16->NumHeads  = NUMBER_OF_HEADS;
    pSBD_BPB16->HiddSec   = HIDDEN_SECTORS;
    pSBD_BPB16->DrvNum    = DRIVER_NUMBER;
    pSBD_BPB16->Reserved1 = RESERVED1;
    pSBD_BPB16->BootSig   = BOOT_SIGNATURE;
    pSBD_BPB16->VolID     = VOLUME_ID;
    memcpy(pSBD_BPB16->VolLab, NO_VOLUME_LABEL, VOLUME_LABEL_LEN);

    if (iFatType == FAT_12) {
        pFD = &FAT12DEF;
    } else {
        pFD = &FAT16DEF;
    }

    memcpy(pSBD_BPB16->FilSysType, pFD->FILE_SYSTEM_TYPE, FS_TYPE_STR_LEN);
    *(pMem + 510) = 0x55;
    *(pMem + 511) = 0xAA;
    InvertFATBPB16(pSBD_BPB16);
    /* write the boot sector back */
    memcpy(pRAMDisk + MBR_SECTOR * 512, pMem, 512);
    /* set FAT[0], FAT[1] */
    memset(pMem, 0x00, 512);

    if (iFatType == FAT_12) {
        /* set FAT0 */
        wTemp = (u16)(pFD->FAT0) & 0xFFFF;
        *pMem = (u8)(wTemp & 0xFF);
        *(pMem + 1) = (u8)((wTemp >> 8) & 0xFF);
        /* set FAT1 */
        wTemp = (u16)(pFD->FAT1) & 0xFFFF;
        *(pMem + 1) &= 0x0F;
        *(pMem + 1) |= (u8)((wTemp & 0x000F) << 4);
        *(pMem + 2) = (u8)(wTemp >> 4);
    } else {
        /* set FAT0 */
        wTemp = (u16)((pFD->FAT0) & 0xFFFF);
        *pMem = (u8)(wTemp & 0xFF);
        *(pMem + 1) = (u8)((wTemp >> 8) & 0xFF);
        /* set FAT1 */
        wTemp = (u16)((pFD->FAT1) & 0xFFFF);
        *(pMem + 2) = (u8)(wTemp & 0xFF);
        *(pMem + 3) = (u8)((wTemp >> 8) & 0xFF);
    }

    /* write FAT table */
    memcpy(pRAMDisk + MBR_SECTOR * 512 + 512, pMem, 512);
    memset(pMem, 0, 512);

    for (j = 1; j < FATSz16; j++) {
        memcpy(pRAMDisk + MBR_SECTOR * 512 + (1 + j) * 512, pMem, 512);
    }

    /* empty the root directory */
    dwRootDirSector = 1 + FATSz16;

    for (j = 0; j < 32; j++) {
        memcpy(pRAMDisk + MBR_SECTOR * 512 + (uint)((dwRootDirSector + j) * 512), pMem, 512);
    }

    free(pMem);

    return  1;
}

#endif /* DEMO_MSTOR && DEMO_MSTOR_RAMDISK */

#if DEMO_AUDIO
#define MIC_CHANNELS 1
#if (MIC_CHANNELS == 1)
#include "soundm.h"
#else
#include "sounds.h"
#endif
static unsigned long g_iAudioTotalReceived = 0;
static unsigned long g_iAudioLastReceived = 0;
static unsigned long usbd_recording = 0;

static void USBDAudioNotification(int port, int notification, int parameter)
{
    char pTempBuf1[16];
    char pTempBuf[64];

    switch (notification) {
#if (SUD_AUDIO_INCLUDE_MIC + SUD_AUDIO_INCLUDE_SPK) > 0

        case SUD_AUDIO_NOTIFY_ISOCDATAREADY:
            g_iAudioTotalReceived += parameter;

            if ((g_iAudioTotalReceived - g_iAudioLastReceived) > 40 * 1024) {
                g_iAudioLastReceived = g_iAudioTotalReceived;
                _ultoa(g_iAudioTotalReceived, pTempBuf1, 10);
                strcpy(pTempBuf, "GotAudioData ");
                strcat(pTempBuf, pTempBuf1);
                USBDPutString(pTempBuf);
            }

            break;
#endif
#if SUD_AUDIO_MIDI_NUMBER

        case SUD_AUDIO_NOTIFY_BULKDATAREADY:
            g_iAudioTotalReceived += parameter;

            if ((g_iAudioTotalReceived - g_iAudioLastReceived) > 1024) {
                g_iAudioLastReceived = g_iAudioTotalReceived;
                _ultoa(g_iAudioTotalReceived, pTempBuf1, 10);
                strcpy(pTempBuf, "Gotten MIDI Data ");
                strcat(pTempBuf, pTempBuf1);
                USBDPutString(pTempBuf);
            }

            break;
#endif
#if (SUD_AUDIO_INCLUDE_MIC + SUD_AUDIO_INCLUDE_SPK) > 0

        case SUD_AUDIO_NOTIFY_SPK_START_STOP:
            if (parameter) {
                g_iAudioTotalReceived = g_iAudioLastReceived = 0;
                USBDPutString("Speaker Started");
            } else {
                _ultoa(g_iAudioTotalReceived, pTempBuf1, 10);
                strcpy(pTempBuf, "Speaker Stopped Total Data ");
                strcat(pTempBuf, pTempBuf1);
                USBDPutString(pTempBuf);
            }

            break;

        case SUD_AUDIO_NOTIFY_MIC_START_STOP:
            if (parameter) {
                g_iAudioTotalReceived = g_iAudioLastReceived = 0;
                USBDPutString("Microphone Started");
            } else {
                USBDPutString("Microphone Stopped");
            }

            usbd_recording = parameter;
            break;
#endif
    }
}

#if SUD_AUDIO_MIDI_NUMBER
void send_midi_data(void)
{
    u8 MIDI[4];

    if (sud_AudioIsConnected(0)) {
        sud_AudioSendMIDIData(0, &MIDI[0], 4);
    }
}
#endif

#if (SUD_AUDIO_INCLUDE_MIC + SUD_AUDIO_INCLUDE_SPK) > 0
static void send_audio_data(void)
{
    uint i;
    uint size;
    uint iPacketSize;
    const u8 *pData;
    /*
    char pTempBuf1[16];
    char pTempBuf[64];
    */
    SUD_AUDIO_SETTINGS Settings;
    uint iAdjustPackets;
    uint iTotalRecorded;
    uint iTotalSize;
    u32 iPacketNum;

    if (usbd_recording) {
        iTotalRecorded = 0;
#if (MIC_CHANNELS == 1)
        iTotalSize = sizeof(pMonoSoundData);
#else
        iTotalSize = sizeof(pStereoSoundData);
#endif
        sud_AudioGetCurMicSettings(0, &Settings);

        if (Settings.iChannels == MIC_CHANNELS && Settings.iBits == 16 && Settings.dwSampleRate == 44100) {
            iPacketNum = 0;

            if (0 != (Settings.dwSampleRate % 1000)) {
                iAdjustPackets = 1000 / (uint)(Settings.dwSampleRate % 1000L);
            } else {
                iAdjustPackets = 0;
            }

#if (MIC_CHANNELS == 1)
            pData = pMonoSoundData;
#else
            pData = pStereoSoundData;
#endif

            while (usbd_recording) {
                iPacketSize = 0;

                for (i = 0; i < 1; i++) {
                    size = Settings.dwSampleRate * (Settings.iBits / 8) * Settings.iChannels;
                    size =  size / 1000;

                    if (iAdjustPackets != 0 && iPacketNum > 0 && 0 == (iPacketNum % iAdjustPackets)) {
                        size += (Settings.iBits / 8) * Settings.iChannels;
                    }

                    iPacketSize += size;
                    iPacketNum++;
                }

                iTotalRecorded += iPacketSize;

                if (iTotalRecorded > iTotalSize) {
#if (MIC_CHANNELS == 1)
                    pData = pMonoSoundData;
#else
                    pData = pStereoSoundData;
#endif
                    iTotalRecorded = 0;
                }

                /*
                g_iAudioTotalReceived += size;
                if((g_iAudioTotalReceived - g_iAudioLastReceived) > size*1000)
                {
                     g_iAudioLastReceived = g_iAudioTotalReceived;
                     _ultoa(g_iAudioTotalReceived, pTempBuf1, 10);
                     strcpy(pTempBuf, "Sent Audio Data ");
                     strcat(pTempBuf, pTempBuf1);
                     USBDPutString(AUDIO_STATUS_LN, pTempBuf);
                }
                */
                if (sud_AudioSendAudioData(0, (u8 *)pData, iPacketSize) != 0) {
                    break;
                }

                pData += iPacketSize;
            }
        }
    }
}
#endif
#endif

#if DEMO_KBD
static u8 KeyCode[] = {
    SUD_KBD_CODE_A,
    SUD_KBD_CODE_B,
    SUD_KBD_CODE_C,
    SUD_KBD_CODE_D,
    SUD_KBD_CODE_E,
    SUD_KBD_CODE_F,
    SUD_KBD_CODE_G,
    SUD_KBD_CODE_H,
    SUD_KBD_CODE_I,
    SUD_KBD_CODE_J,
    SUD_KBD_CODE_K,
    SUD_KBD_CODE_L,
    SUD_KBD_CODE_M,
    SUD_KBD_CODE_N,
    SUD_KBD_CODE_O,
    SUD_KBD_CODE_P,
    SUD_KBD_CODE_Q,
    SUD_KBD_CODE_R,
    SUD_KBD_CODE_S,
    SUD_KBD_CODE_T,
    SUD_KBD_CODE_U,
    SUD_KBD_CODE_V,
    SUD_KBD_CODE_W,
    SUD_KBD_CODE_X,
    SUD_KBD_CODE_Y,
    SUD_KBD_CODE_Z,
    SUD_KBD_CODE_1,
    SUD_KBD_CODE_2,
    SUD_KBD_CODE_3,
    SUD_KBD_CODE_4,
    SUD_KBD_CODE_5,
    SUD_KBD_CODE_6,
    SUD_KBD_CODE_7,
    SUD_KBD_CODE_8,
    SUD_KBD_CODE_9,
    SUD_KBD_CODE_0,
};

static void send_kbd_event(void)
{
    static int i = 0;
    char pTempBuf[64];
    char pTempBuf1[32];
    u8 KBDData[6];

    memset(KBDData, 0, 6);

    KBDData[0] = KeyCode[i % sizeof(KeyCode)];

    if (sud_KBDInput(0, KBDData, 6)) {
        sb_OS_WAIT_MSEC_MT(50);
        KBDData[0] = 0;

        if (sud_KBDInput(0, KBDData, 6)) {
            _ultoa(i++, pTempBuf1, 10);
            strcpy(pTempBuf, "Sending Keyboard Event ");
            strcat(pTempBuf, pTempBuf1);
            USBDPutString(pTempBuf);
        }
    }

    sb_OS_WAIT_MSEC_MT(50);
}
#endif

#if DEMO_DFU
uint g_iLastBlock = 0;

static int DemoDFUInit(uint segment)
{
    return SUD_DFU_STATUS_OK;
}

static int DemoDFUWrite(uint segment, u32 dwOffset, u8 *pData, uint iSize)
{
    g_iLastBlock = 0;
    g_iDFUWrite = 1;
    return iSize;
}

static int DemoDFURead(uint segment, u32 dwOffset, u8 *pData, uint iSize)
{
    if (dwOffset >= 32 * 1024) {
        iSize = 0;
    } else {
        memset(pData, (u8)(dwOffset >> 8), iSize);
        USBDPutString("ReadBlock");
    }

    return iSize;
}

static int DemoDFUDone(uint segment)
{
    g_iLastBlock = 1;
    g_iDFUWrite = 1;
    return SUD_DFU_STATUS_OK;
}

static void DFUDo(void)
{
    if (g_iDFUWrite) {
        /* simulate the flash write operation */
        USBDPutString(g_iLastBlock ? "Manifestation" : "WriteBlock");
        sb_OS_WAIT_MSEC_MT(100);
        USBDPutString(g_iLastBlock ? "Manifestation Done" : "WriteBlock Done");
        g_iDFUWrite = 0;
        sud_DFUWriteDone(SUD_DFU_STATUS_OK, g_iLastBlock ? SUD_DFU_MANIFESTATION_DONE : SUD_DFU_WRITE_BLOCK_DONE);
    }

}

#endif

#if DEMO_HIDCOM
static char g_pHIDBuf[SUD_HID_IN_PACKET_SIZE];
static uint g_iReportID;
static uint g_iHIDSize;
static void HIDOutputNotify(uint iReportID, void *pDataBuf, uint size)
{
    USBDPutString("Output Report Received");
    memcpy(g_pHIDBuf, pDataBuf, SB_MIN(size, SUD_HID_IN_PACKET_SIZE));
    g_iReportID = iReportID;
    g_iHIDSize = size;
    g_iHIDReady = 1;
}

static void HIDComDo(void)
{
    if (g_iHIDReady) {
        g_iHIDReady = 0;

        if (sud_HIDSendInput(g_iReportID, g_pHIDBuf, g_iHIDSize)) {
            USBDPutString("Sent HID Input report");
        }
    }
}
#endif

#if DEMO_MTP

static char g_szMTPFileName[260];
static char g_pMTPTempBuf[260];
static char g_DataBuf[512];

#if __packed_pragma
#pragma pack(1)  /* pack structures (byte alignment) */
#endif

/*
  code to parse the EXIF/JPEG file information and thumbnail image
  Not fully tested, only tested for Nikon D60 camera generated files
*/
typedef struct {
    u8 SOI[2];          /* 00h  Start of Image Marker     */
    u8 APP1[2];         /* 02h  Application Use Marker    */
    u8 Length[2];       /* 04h  Length of APP1 Field      */
    u8 EXIF[5];
    u8 IFD[1];
} EXIFHEAD;

typedef struct {
    u8 APP[2];          /* 00h  Application Use Marker    */
    u8 Length[2];       /* 02h  Length of APP1 Field      */
} EXIFMARKER;

typedef struct {
    u8 DataBits[1];
    u8 Vertical[2];
    u8 Horizontal[2];
    u8 Components[1];
} EXIFSOF;

typedef struct {
    u8 ByteOrder[2];
    u8 Fixed[2];
    u8 Offset[4];
} TIFFHEAD;

typedef struct {
    u8 Tag[2];
    u8 Type[2];
    u8 Count[4];
    u8 Offset[4];
} IFD;

#if __packed_pragma
#pragma pack()
#endif

static int GetExifJPEGInfo(char *pFileName, uint *piWidth, uint *piHeight, uint *piBits, uint *piThumbSize, uint *piThumbFormat, uint *piThumbWidth, uint *piThumbHeight)
{
    FILEHANDLE  fp1;
    uint wOffset;
    uint wThumbNeilOffset;
    u32 dwTemp;
    u16 wTemp;
    u8 cTemp[4];
    int i;
    int exit = 0;
    int result = -1;
    long pos;

    EXIFHEAD Header;
    EXIFMARKER Marker;
    EXIFSOF Sof;
    TIFFHEAD TiffHeader;
    IFD ifd;
    fp1 = sfs_fopen(pFileName, "rb");

    if (fp1) {
        sfs_fread(&Header, sizeof(EXIFHEAD), 1, fp1);

        if (Header.SOI[0] == 0xFF && Header.SOI[1] == 0xD8 && Header.APP1[0] == 0xFF && Header.APP1[1] == 0xE1) {
            wOffset = ((uint)Header.Length[1] | ((uint)Header.Length[0] << 8)) + 4;
            /* check the APP1 marker to get the thumbnail information */
            sfs_fread(&TiffHeader, sizeof(TIFFHEAD), 1, fp1);
            dwTemp = ((u32)TiffHeader.Offset[0] << 24) | ((u32)TiffHeader.Offset[1] << 16) | ((u32)TiffHeader.Offset[2] << 8) | ((u32)TiffHeader.Offset[3]);

            if (dwTemp > 8) {
                sfs_fseek(fp1, dwTemp - 8, SFS_SEEK_CUR);
            }

            /* 0th IFD */
            sfs_fread(cTemp, 2, 1, fp1);
            wTemp = ((uint)cTemp[0] << 8 | (uint)cTemp[1]);

            for (i = 0; i < wTemp; i++) {
                sfs_fread(&ifd, sizeof(IFD), 1, fp1);
            }

            /* Next IFD */
            sfs_fread(cTemp, 4, 1, fp1);
            dwTemp = ((u32)cTemp[0] << 24) | ((u32)cTemp[1] << 16) | ((u32)cTemp[2] << 8) | ((u32)cTemp[3]);

            if (dwTemp != 0) {
                /* it should be exif IFD */
                sfs_fseek(fp1, dwTemp + sizeof(EXIFHEAD), SFS_SEEK_SET);
                sfs_fread(cTemp, 2, 1, fp1);
                wTemp = ((uint)cTemp[0] << 8 | (uint)cTemp[1]);

                for (i = 0; i < wTemp; i++) {
                    sfs_fread(&ifd, sizeof(IFD), 1, fp1);
                }

                /* Next IFD */
                sfs_fread(cTemp, 4, 1, fp1);
                dwTemp = ((u32)cTemp[0] << 24) | ((u32)cTemp[1] << 16) | ((u32)cTemp[2] << 8) | ((u32)cTemp[3]);
                /* 1st IFD */
                sfs_fread(cTemp, 2, 1, fp1);
                wTemp = ((uint)cTemp[0] << 8 | (uint)cTemp[1]);
                /* now we should skip the JPEGInterchangeFormat and JPEGInterchangeFormatLength*/
                sfs_fseek(fp1, 4 * sizeof(u32), SFS_SEEK_CUR);
                pos = sfs_ftell(fp1);

                if (pos > 0) {
                    *piThumbSize = (uint)pos;
                } else {
                    *piThumbSize = 0;
                }

                sfs_fread(&Header, sizeof(EXIFHEAD), 1, fp1);

                if (Header.SOI[0] == 0xFF && Header.SOI[1] == 0xD8 && Header.APP1[0] == 0xFF && Header.APP1[1] == 0xDB) {
                    /* should be Define Quantization Table */
                    wThumbNeilOffset = ((uint)Header.Length[1] | ((uint)Header.Length[0] << 8)) + 4 - sizeof(EXIFHEAD);
                    sfs_fseek(fp1, wThumbNeilOffset, SFS_SEEK_CUR);
                    exit = 0;

                    while (!sfs_feof(fp1) && !exit) {
                        sfs_fread(&Marker, sizeof(EXIFMARKER), 1, fp1);

                        if (Marker.APP[0] == 0xFF) {
                            switch (Marker.APP[1]) {
                                case 0xC0:
                                    sfs_fread(&Sof, sizeof(EXIFSOF), 1, fp1);
                                    *piThumbWidth = ((uint)Sof.Horizontal[1] | ((uint)Sof.Horizontal[0] << 8));
                                    *piThumbHeight = ((uint)Sof.Vertical[1] | ((uint)Sof.Vertical[0] << 8));
                                    *piThumbFormat = SUD_MTP_OBJ_FMT_IMG_JFIF;
                                    exit = 1;
                                    break;
                            }

                            wThumbNeilOffset = ((uint)Marker.Length[1] | ((uint)Marker.Length[0] << 8)) + 2;
                            sfs_fseek(fp1, wThumbNeilOffset, SFS_SEEK_CUR);
                        }
                    }

                    exit = 0;

                    /* scan the whole thumbnail image to find out the EOI, need to find a simple way to get the thumbnail image size */
                    while (!sfs_feof(fp1)) {
                        sfs_fread(g_DataBuf, 512, 1, fp1);

                        for (i = 0; i < 512; i++) {
                            if (g_DataBuf[i] == 0xFF && g_DataBuf[i + 1] == 0xD9) {
                                pos = sfs_ftell(fp1) - *piThumbSize - (512 - i - 2);

                                if (pos > 0) {
                                    *piThumbSize = (uint)pos;
                                } else {
                                    *piThumbSize = 0;
                                }

                                exit = 1;
                                break;
                            }
                        }

                        if (exit) {
                            break;
                        }
                    }
                }
            }

            sfs_fseek(fp1, wOffset, SFS_SEEK_SET);
            exit = 0;

            while (!sfs_feof(fp1) && !exit) {
                sfs_fread(&Marker, sizeof(EXIFMARKER), 1, fp1);

                if (Marker.APP[0] == 0xFF) {
                    switch (Marker.APP[1]) {
                        case 0xC0:
                            sfs_fread(&Sof, sizeof(EXIFSOF), 1, fp1);
                            *piWidth = ((uint)Sof.Horizontal[1] | ((uint)Sof.Horizontal[0] << 8));
                            *piHeight = ((uint)Sof.Vertical[1] | ((uint)Sof.Vertical[0] << 8));
                            *piBits = ((uint)Sof.DataBits[0]) * ((uint)Sof.Components[0]);
                            exit = 1;
                            result = 0;
                            break;

                        default:
                        case 0xD9:
                            break;
                    }

                    wOffset += ((uint)Marker.Length[1] | ((uint)Marker.Length[0] << 8)) + 2;
                    sfs_fseek(fp1, wOffset, SFS_SEEK_SET);
                } else {
                    break;
                }
            }
        }

        sfs_fclose(fp1);
    }

    return result;

}

static int SMXMTPDiskMounted(uint index)
{
    return ((sfs_devstatus(index) & (~SFS_DEVICE_NOT_SHUT_DOWN)) == SFS_DEVICE_MOUNTED);
}

static int SMXMTPStorageNum(void)
{
    uint total = 0;
    uint i;

    for (i = 0; i < SFS_MAX_DEV_NUM; i++) {
        /* Check if the disk driver registered */
        if (sfs_getdev(i)) {
            total++;
        }
    }

    return (int)total;
}

static void *SMXMTPOpen(uint index, char *pFileName, uint iMode)
{
    g_szMTPFileName[0] = SFS_FIRST_DRIVE + index;
    g_szMTPFileName[1] = ':';
    strcpy(&g_szMTPFileName[2], pFileName);
    strcpy(g_pMTPTempBuf, "Open ");
    strcat(g_pMTPTempBuf, g_szMTPFileName);
    USBDPutString(g_pMTPTempBuf);
    return (void *)sfs_fopen(g_szMTPFileName, (iMode == SUD_MTP_OBJ_MODE_READONLY) ? "r" : "r+");
}

static int SMXMTPClose(void *pFileHandle)
{
    return sfs_fclose((FILEHANDLE)pFileHandle);
}

static int SMXMTPRead(u8 *pRAMAddr, u32 dwSize, void *pFileHandle)
{
    return sfs_fread(pRAMAddr, 1, dwSize, (FILEHANDLE)pFileHandle);
}

static int SMXMTPWrite(u8 *pRAMAddr, u32 dwSize, void *pFileHandle)
{
    return sfs_fwrite(pRAMAddr, 1, dwSize, (FILEHANDLE)pFileHandle);
}

static int SMXMTPMkDir(uint index, char *pPathName)
{
    g_szMTPFileName[0] = SFS_FIRST_DRIVE + index;
    g_szMTPFileName[1] = ':';
    strcpy(&g_szMTPFileName[2], pPathName);
    return sfs_mkdir(g_szMTPFileName);
}

static int SMXMTPRmDir(uint index, char *pPathName)
{
    g_szMTPFileName[0] = SFS_FIRST_DRIVE + index;
    g_szMTPFileName[1] = ':';
    strcpy(&g_szMTPFileName[2], pPathName);
    return sfs_rmdir(g_szMTPFileName);
}

static int SMXMTPDelete(uint index, char *pFileName)
{
    g_szMTPFileName[0] = SFS_FIRST_DRIVE + index;
    g_szMTPFileName[1] = ':';
    strcpy(&g_szMTPFileName[2], pFileName);
    strcpy(g_pMTPTempBuf, "Delete ");
    strcat(g_pMTPTempBuf, g_szMTPFileName);
    USBDPutString(g_pMTPTempBuf);
    return sfs_fdelete(g_szMTPFileName);
}

static int SMXMTPTotalSize(uint index)
{
    return sfs_totalkb(index);
}

static int SMXMTPFreeSize(uint index)
{
    return sfs_freekb(index);
}

static int SMXMTPFindFirst(uint index, char *pFindSpec, SUD_MTP_FILE_INFO *pFileInfo)
{
    int id = -1;
    FILEINFO *pSMXFSFileinfo;
    pSMXFSFileinfo = (FILEINFO *)sb_OS_MEM_ALLOC(sizeof(FILEINFO));

    if (pSMXFSFileinfo) {
        g_szMTPFileName[0] = SFS_FIRST_DRIVE + index;
        g_szMTPFileName[1] = ':';
        strcpy(&g_szMTPFileName[2], pFindSpec);
        pFileInfo->pPrivate = pSMXFSFileinfo;
        id = sfs_findfirst(g_szMTPFileName, pSMXFSFileinfo);

        if (id != -1) {
            pFileInfo->attr = 0;
            strcpy((char *)pFileInfo->name, (char *)pSMXFSFileinfo->name);

            if (pSMXFSFileinfo->st_mode & S_IFDIR) {
                pFileInfo->attr |= SUD_MTP_OBJ_ATTR_DIR;
            }

            pFileInfo->st_size_l = pSMXFSFileinfo->st_size;
            pFileInfo->st_size_h = 0;
            pFileInfo->st_ctime.wYear = pSMXFSFileinfo->st_ctime.wYear;
            pFileInfo->st_ctime.wMonth = pSMXFSFileinfo->st_ctime.wMonth;
            pFileInfo->st_ctime.wDay = pSMXFSFileinfo->st_ctime.wDay;
            pFileInfo->st_ctime.wHour = pSMXFSFileinfo->st_ctime.wHour;
            pFileInfo->st_ctime.wMinute = pSMXFSFileinfo->st_ctime.wMinute;
            pFileInfo->st_ctime.wSecond = pSMXFSFileinfo->st_ctime.wSecond;
            pFileInfo->st_mtime.wYear = pSMXFSFileinfo->st_mtime.wYear;
            pFileInfo->st_mtime.wMonth = pSMXFSFileinfo->st_mtime.wMonth;
            pFileInfo->st_mtime.wDay = pSMXFSFileinfo->st_mtime.wDay;
            pFileInfo->st_mtime.wHour = pSMXFSFileinfo->st_mtime.wHour;
            pFileInfo->st_mtime.wMinute = pSMXFSFileinfo->st_mtime.wMinute;
            pFileInfo->st_mtime.wSecond = pSMXFSFileinfo->st_mtime.wSecond;
            pFileInfo->st_atime.wYear = pSMXFSFileinfo->st_atime.wYear;
            pFileInfo->st_atime.wMonth = pSMXFSFileinfo->st_atime.wMonth;
            pFileInfo->st_atime.wDay = pSMXFSFileinfo->st_atime.wDay;
            pFileInfo->st_atime.wHour = pSMXFSFileinfo->st_atime.wHour;
            pFileInfo->st_atime.wMinute = pSMXFSFileinfo->st_atime.wMinute;
            pFileInfo->st_atime.wSecond = pSMXFSFileinfo->st_atime.wSecond;
            /*
            strcpy(g_pMTPTempBuf, "Find ");
            strcat(g_pMTPTempBuf, (char *)pFileInfo->name);
            USBDPutString(g_pMTPTempBuf);
            */
        }
    }

    return id;
}
static int SMXMTPFindNext(uint iID, SUD_MTP_FILE_INFO *pFileInfo)
{
    int id;
    FILEINFO *pSMXFSFileinfo = (FILEINFO *)pFileInfo->pPrivate;

    if (pSMXFSFileinfo) {
        id = sfs_findnext((int)iID, pSMXFSFileinfo);

        if (id != -1) {
            pFileInfo->attr = 0;
            strcpy((char *)pFileInfo->name, (char *)pSMXFSFileinfo->name);

            if (pSMXFSFileinfo->st_mode & S_IFDIR) {
                pFileInfo->attr |= SUD_MTP_OBJ_ATTR_DIR;
            }

            pFileInfo->st_size_l = pSMXFSFileinfo->st_size;
            pFileInfo->st_size_h = 0;
            pFileInfo->st_ctime.wYear = pSMXFSFileinfo->st_ctime.wYear;
            pFileInfo->st_ctime.wMonth = pSMXFSFileinfo->st_ctime.wMonth;
            pFileInfo->st_ctime.wDay = pSMXFSFileinfo->st_ctime.wDay;
            pFileInfo->st_ctime.wHour = pSMXFSFileinfo->st_ctime.wHour;
            pFileInfo->st_ctime.wMinute = pSMXFSFileinfo->st_ctime.wMinute;
            pFileInfo->st_ctime.wSecond = pSMXFSFileinfo->st_ctime.wSecond;
            pFileInfo->st_mtime.wYear = pSMXFSFileinfo->st_mtime.wYear;
            pFileInfo->st_mtime.wMonth = pSMXFSFileinfo->st_mtime.wMonth;
            pFileInfo->st_mtime.wDay = pSMXFSFileinfo->st_mtime.wDay;
            pFileInfo->st_mtime.wHour = pSMXFSFileinfo->st_mtime.wHour;
            pFileInfo->st_mtime.wMinute = pSMXFSFileinfo->st_mtime.wMinute;
            pFileInfo->st_mtime.wSecond = pSMXFSFileinfo->st_mtime.wSecond;
            pFileInfo->st_atime.wYear = pSMXFSFileinfo->st_atime.wYear;
            pFileInfo->st_atime.wMonth = pSMXFSFileinfo->st_atime.wMonth;
            pFileInfo->st_atime.wDay = pSMXFSFileinfo->st_atime.wDay;
            pFileInfo->st_atime.wHour = pSMXFSFileinfo->st_atime.wHour;
            pFileInfo->st_atime.wMinute = pSMXFSFileinfo->st_atime.wMinute;
            pFileInfo->st_atime.wSecond = pSMXFSFileinfo->st_atime.wSecond;
            /*
            strcpy(g_pMTPTempBuf, "Find ");
            strcat(g_pMTPTempBuf, (char *)pFileInfo->name);
            USBDPutString(g_pMTPTempBuf);
            */
        }
    } else {
        id = -1;
    }

    return id;
}

static int SMXMTPFindClose(SUD_MTP_FILE_INFO *pFileInfo)
{
    if (pFileInfo->pPrivate) {
        int ret;
        ret = sfs_findclose((FILEINFO *)(pFileInfo->pPrivate));
        sb_OS_MEM_FREE(pFileInfo->pPrivate);
        return ret;
    }

    return 0;
}

static int SMXMTPGetImgProp(uint index, char *pFileName, uint *piWidth, uint *piHeight, uint *piBits, uint *piThumbSize, uint *piThumbFormat, uint *piThumbWidth, uint *piThumbHeight)
{
    g_szMTPFileName[0] = SFS_FIRST_DRIVE + index;
    g_szMTPFileName[1] = ':';
    strcpy(&g_szMTPFileName[2], pFileName);
    *piWidth = 640;
    *piHeight = 480;
    *piBits = 24;
    *piThumbFormat = 0;
    *piThumbSize = 0;
    *piThumbWidth = 0;
    *piThumbHeight = 0;

    GetExifJPEGInfo(g_szMTPFileName, piWidth, piHeight, piBits, piThumbSize, piThumbFormat, piThumbWidth, piThumbHeight);
    return 0;
}

static void *SMXMTPOpenImgThumb(uint index, char *pFileName)
{
    u32 dwTemp;
    u16 wTemp;
    u8 cTemp[4];
    int i;
    EXIFHEAD Header;
    TIFFHEAD TiffHeader;
    IFD ifd;
    FILEHANDLE fp1;
    g_szMTPFileName[0] = SFS_FIRST_DRIVE + index;
    g_szMTPFileName[1] = ':';
    strcpy(&g_szMTPFileName[2], pFileName);
    fp1 = sfs_fopen(g_szMTPFileName, "r");

    if (fp1) {
        sfs_fread(&Header, sizeof(EXIFHEAD), 1, fp1);

        if (Header.SOI[0] == 0xFF && Header.SOI[1] == 0xD8 && Header.APP1[0] == 0xFF && Header.APP1[1] == 0xE1) {
            sfs_fread(&TiffHeader, sizeof(TIFFHEAD), 1, fp1);
            dwTemp = ((u32)TiffHeader.Offset[0] << 24) | ((u32)TiffHeader.Offset[1] << 16) | ((u32)TiffHeader.Offset[2] << 8) | ((u32)TiffHeader.Offset[3]);

            if (dwTemp > 8) {
                sfs_fseek(fp1, dwTemp - 8, SFS_SEEK_CUR);
            }

            /* 0th IFD */
            sfs_fread(cTemp, 2, 1, fp1);
            wTemp = ((uint)cTemp[0] << 8 | (uint)cTemp[1]);

            for (i = 0; i < wTemp; i++) {
                sfs_fread(&ifd, sizeof(IFD), 1, fp1);
            }

            /* Next IFD */
            sfs_fread(cTemp, 4, 1, fp1);
            dwTemp = ((u32)cTemp[0] << 24) | ((u32)cTemp[1] << 16) | ((u32)cTemp[2] << 8) | ((u32)cTemp[3]);

            if (dwTemp != 0) {
                /* it should be exif IFD */
                sfs_fseek(fp1, dwTemp + sizeof(EXIFHEAD), SFS_SEEK_SET);
                sfs_fread(cTemp, 2, 1, fp1);
                wTemp = ((uint)cTemp[0] << 8 | (uint)cTemp[1]);

                for (i = 0; i < wTemp; i++) {
                    sfs_fread(&ifd, sizeof(IFD), 1, fp1);
                }

                /* Next IFD */
                sfs_fread(cTemp, 4, 1, fp1);
                dwTemp = ((u32)cTemp[0] << 24) | ((u32)cTemp[1] << 16) | ((u32)cTemp[2] << 8) | ((u32)cTemp[3]);
                /* 1st IFD */
                sfs_fread(cTemp, 2, 1, fp1);
                wTemp = ((uint)cTemp[0] << 8 | (uint)cTemp[1]);
                /* now we should skip the JPEGInterchangeFormat and JPEGInterchangeFormatLength*/
                sfs_fseek(fp1, 4 * sizeof(u32), SFS_SEEK_CUR);
                sfs_fread(&Header, sizeof(EXIFHEAD), 1, fp1);

                if (Header.SOI[0] == 0xFF && Header.SOI[1] == 0xD8 && Header.APP1[0] == 0xFF && Header.APP1[1] == 0xDB) {
                    sfs_fseek(fp1, (0l - (long)sizeof(EXIFHEAD)), SFS_SEEK_CUR);
                    return (void *)fp1;
                }
            }
        }
    }

    if (fp1) {
        sfs_fclose(fp1);
    }

    return NULL;
}

static int SMXMTPReadImgThumb(u8 *pRAMAddr, u32 dwSize, void *pHandle)
{
    u32 size;
    u32 i, j;

    for (i = 0; i < dwSize; i += 512) {
        size = dwSize - i;

        if (size > 512) {
            size = 512;
        }

        sfs_fread(pRAMAddr + i, size, 1, (FILEHANDLE)pHandle);

        for (j = 0; j < size; j++) {
            if (*(u8 *)(pRAMAddr + i + j) == 0xFF && *(u8 *)(pRAMAddr + i + j + 1) == 0xD9) {
                dwSize = i + j + 2;
                break;
            }
        }
    }

    return dwSize;
}

static int SMXMTPCloseImgThumb(void *pHandle)
{
    return  sfs_fclose((FILEHANDLE)pHandle);
}

static int SMXMTPGetFormat(uint index, char *pFileName, u16 *pwFormat)
{
    strcpy(g_szMTPFileName, pFileName);
    _strupr(g_szMTPFileName);

    if (strstr((const char *)g_szMTPFileName, ".JPG") || strstr((const char *)g_szMTPFileName, ".JPEG")) {
        *pwFormat = SUD_MTP_OBJ_FMT_IMG_JPEG;
    } else if (strstr((const char *)g_szMTPFileName, ".TIF") || strstr((const char *)g_szMTPFileName, ".TIFF")) {
        *pwFormat = SUD_MTP_OBJ_FMT_IMG_TIFF;
    } else if (strstr((const char *)g_szMTPFileName, ".BMP")) {
        *pwFormat = SUD_MTP_OBJ_FMT_IMG_BMP;
    } else if (strstr((const char *)g_szMTPFileName, ".PNG")) {
        *pwFormat = SUD_MTP_OBJ_FMT_IMG_PNG;
    } else if (strstr((const char *)g_szMTPFileName, ".GID")) {
        *pwFormat = SUD_MTP_OBJ_FMT_IMG_GIF;
    } else if (strstr((const char *)g_szMTPFileName, ".EXE")) {
        *pwFormat = SUD_MTP_OBJ_FMT_EXEC;
    } else if (strstr((const char *)g_szMTPFileName, ".TXT")) {
        *pwFormat = SUD_MTP_OBJ_FMT_TEXT;
    } else if (strstr((const char *)g_szMTPFileName, ".HTM") || strstr((const char *)g_szMTPFileName, ".HTML")) {
        *pwFormat = SUD_MTP_OBJ_FMT_HTML;
    } else if (strstr((const char *)g_szMTPFileName, ".WAV")) {
        *pwFormat = SUD_MTP_OBJ_FMT_WAV;
    } else if (strstr((const char *)g_szMTPFileName, ".AIFF")) {
        *pwFormat = SUD_MTP_OBJ_FMT_AIFF;
    } else if (strstr((const char *)g_szMTPFileName, ".MP3")) {
        *pwFormat = SUD_MTP_OBJ_FMT_MP3;
    } else if (strstr((const char *)g_szMTPFileName, ".AVI")) {
        *pwFormat = SUD_MTP_OBJ_FMT_AVI;
    } else if (strstr((const char *)g_szMTPFileName, ".MPEG")) {
        *pwFormat = SUD_MTP_OBJ_FMT_MPEG;
    } else if (strstr((const char *)g_szMTPFileName, ".ASF")) {
        *pwFormat = SUD_MTP_OBJ_FMT_ASF;
    } else {
        *pwFormat = SUD_MTP_OBJ_FMT_UNDEF;
    }

    return 0;
}

static int SMXMTPInitCapture(uint index, uint format)
{
    /* TODO implement it */
    return 0;
}

static int SMXMTPStartCapture(void)
{
    /* TODO implement it */
    return 0;
}

static int SMXMTPStopCapture(void)
{
    /* TODO implement it */
    return 0;
}
#endif

#if DEMO_VIDEO
#define MDI_ALL_FORMAT_TEST        0
#if SUD_VIDEO_IN_FORMAT_UNCOMPRESSED
#if MDI_ALL_FORMAT_TEST /* for MDI test purpose, you need to generate those video data by yourself */
#include "yuv420_160x120.h"
#include "yuv422_160x120_1.h"
#include "yuv422_1280x720.h"
#else
#include "yuv422_160x120.h"
#endif
#endif

#if SUD_VIDEO_IN_FORMAT_MJPEG
#if MDI_ALL_FORMAT_TEST /* for MDI test purpose, you need to generate those video data by yourself */
#include "mjpeg_160x120_0.h"
#include "mjpeg_160x120_1.h"
#endif
#endif
extern void vpu_function_init();
extern void vpu_function_test();

static SUD_VIDEO_FORMAT g_VideoFormat;
int usb_flag = 1;
int usb_vpu_on = 0;
int usb_stop_flag = 0;

static void USBDVideoNotification(int port, int notification, u32 parameter)
{
    // SUD_VIDEO_SETTINGS *pSettings = NULL;
    
    switch (notification) {
        case SUD_VIDEO_NOTIFY_ISOCDATAREADY:
            USBDPutString("Get Video Out Data");
            break;

        case SUD_VIDEO_NOTIFY_IN_START_STOP:
            usbd_video_recording = parameter;
            if (parameter) {
				usb_flag = 0;
				usb_vpu_on =1;
				isp_start(0);
                isp_start(1);
                USBDPutString("In Started");
            } else {
				usb_vpu_on = 0;
				if(!usb_stop_flag){
                   usb_flag = 1;
				}
                isp_stop(1);
                USBDPutString("In Stopped");
            }

            break;

        case SUD_VIDEO_NOTIFY_OUT_START_STOP:
            if (parameter) {
                USBDPutString("OUT Started");
            } else {
                USBDPutString("OUT Stopped");
            }

            break;

        case SUD_VIDEO_NOTIFY_INIT_DEF_SETTINGS:
            USBDPutString("Set default settings");
            break;

        case SUD_VIDEO_NOTIFY_INIT_MIN_SETTINGS:
            USBDPutString("Set min settings");
            break;

        case SUD_VIDEO_NOTIFY_INIT_MAX_SETTINGS:
            USBDPutString("Set max settings");
            break;

        case SUD_VIDEO_NOTIFY_INIT_INFO_SETTINGS:
            USBDPutString("Set info settings");
            break;

        case SUD_VIDEO_NOTIFY_SET_SETTINGS:
            USBDPutString("Set settings changed");
#if 0
            pSettings = (SUD_VIDEO_SETTINGS *)parameter;
            if (!pSettings)
            {
                USBDPutString("Settings changed: NULL parameter");
            }

            printf("Settings changed: \r\n       \
                            pSettings->bDevicePowerMode %d \r\n \
                            pSettings->bScanningMode %d \r\n \
                            pSettings->bAutoExposureMode %d \r\n \
                            pSettings->bAutoExposurePriority %d \r\n \
                            pSettings->dwExposureTimeAbsolute %d \r\n \
                            pSettings->bExposureTimeRelative %d \r\n \
                            pSettings->wFocusAbsolute %d \r\n \
                            pSettings->bFocusRelative %d \r\n \
                            pSettings->bFocusRelativeSpeed %d \r\n \
                            pSettings->bFocusAuto %d \r\n \
                            pSettings->wIrisAbsolute %d \r\n \
                            pSettings->bIrisRelative %d \r\n \
                            pSettings->wObjectiveFocalLength %d \r\n \
                            pSettings->bZoom %d \r\n \
                            pSettings->bDigitalZoom %d \r\n \
                            pSettings->bZoomSpeed %d \r\n \
                            pSettings->dwPanAbsolute %d \r\n \
                            pSettings->dwTiltAbsolute %d \r\n \
                            pSettings->bPanRelative %d \r\n \
                            pSettings->bPanSpeed %d \r\n \
                            pSettings->bTiltRelative %d \r\n \
                            pSettings->bTiltSpeed %d \r\n \
                            pSettings->wRollAbsolute %d \r\n \
                            pSettings->bRollRelative %d \r\n \
                            pSettings->bRollRelativeSpeed %d \r\n \
                            pSettings->bPrivacy %d \r\n \
                            pSettings->Selector %d \r\n \
                            pSettings->wBacklightCompensation %d \r\n \
                            pSettings->wBrightness %d \r\n \
                            pSettings->wContrast %d \r\n \
                            pSettings->wGain %d \r\n \
                            pSettings->bPowerLineFrequency %d \r\n \
                            pSettings->wHue %d \r\n \
                            pSettings->bHueAuto %d \r\n \
                            pSettings->wSaturation %d \r\n \
                            pSettings->wSharpness %d \r\n \
                            pSettings->wGamma %d \r\n \
                            pSettings->wWhiteBalanceTemperature %d \r\n \
                            pSettings->bWhiteBalanceTemperatureAuto %d \r\n \
                            pSettings->wWhiteBalanceBlue %d \r\n \
                            pSettings->wWhiteBalanceRed %d \r\n \
                            pSettings->bWhiteBalanceComponentAuto %d \r\n \
                            pSettings->wMultiplierStep %d \r\n \
                            pSettings->wMultiplierLimit %d \r\n \
                            pSettings->bVideoStandard %d \r\n \
                            pSettings->bAnalogVideoLockStatus %d \r\n \
                            pSettings->wSyncDelay %d \r\n \
                            ",
                            pSettings->bDevicePowerMode,
                            pSettings->bScanningMode,
                            pSettings->bAutoExposureMode,
                            pSettings->bAutoExposurePriority,
                            pSettings->dwExposureTimeAbsolute,
                            pSettings->bExposureTimeRelative,
                            pSettings->wFocusAbsolute,
                            pSettings->bFocusRelative,
                            pSettings->bFocusRelativeSpeed,
                            pSettings->bFocusAuto,
                            pSettings->wIrisAbsolute,
                            pSettings->bIrisRelative,
                            pSettings->wObjectiveFocalLength,
                            pSettings->bZoom,
                            pSettings->bDigitalZoom,
                            pSettings->bZoomSpeed,
                            pSettings->dwPanAbsolute,
                            pSettings->dwTiltAbsolute,
                            pSettings->bPanRelative,
                            pSettings->bPanSpeed,
                            pSettings->bTiltRelative,
                            pSettings->bTiltSpeed,
                            pSettings->wRollAbsolute,
                            pSettings->bRollRelative,
                            pSettings->bRollRelativeSpeed,
                            pSettings->bPrivacy,
                            pSettings->bSelector,
                            pSettings->wBacklightCompensation,
                            pSettings->wBrightness,
                            pSettings->wContrast,
                            pSettings->wGain,
                            pSettings->bPowerLineFrequency,
                            pSettings->wHue,
                            pSettings->bHueAuto,
                            pSettings->wSaturation,
                            pSettings->wSharpness,
                            pSettings->wGamma,
                            pSettings->wWhiteBalanceTemperature,
                            pSettings->bWhiteBalanceTemperatureAuto,
                            pSettings->wWhiteBalanceBlue,
                            pSettings->wWhiteBalanceRed,
                            pSettings->bWhiteBalanceComponentAuto,
                            pSettings->wMultiplierStep,
                            pSettings->wMultiplierLimit,
                            pSettings->bVideoStandard,
                            pSettings->bAnalogVideoLockStatus,
                            pSettings->wSyncDelay);
#endif
            break;

        case SUD_VIDEO_NOTIFY_SET_PROBE_FORMAT:		
            memcpy(&g_VideoFormat, (void *)parameter, sizeof(SUD_VIDEO_FORMAT));
#if 0
            printf("Probe format changed: interval: %d, TranSize: %d, FrameSize: %d, Format: %d, h: %d, w: %d\n",
                            g_VideoFormat.dwFrameInterval,
                            g_VideoFormat.dwMaxPayloadTransferSize,
                            g_VideoFormat.dwMaxVideoFrameSize,
                            g_VideoFormat.iFormat,
                            g_VideoFormat.wHeight,
                            g_VideoFormat.wWidth);
#endif
            break;

        case SUD_VIDEO_NOTIFY_SET_STILL_FORMAT:
            USBDPutString("Still format changed");
            break;
    }
}

int *UVCImageSource()
{
    //lfcui
#if 0    
    if(g_str_image_source.type == IR)
    {
        return (u8 *)MM_MIPI2DMA_BUF1;  //ir
    }
    else if(g_str_image_source.type == RGB)
    {
        return (u8 *)MM_IMG_ADDR_END;  //rgb
    }
    else if(g_str_image_source.type == DEPTH)
    {
        aos_msleep(1000);
    }
#endif
    return NULL;
}
extern uint8_t g_sensor_type;

void VideoDo(void)
{
    u8 *pVideoData = NULL;
    u32 dwCurrentMSec;
    // u32 dwRemainnMSec;
    uint iTotalSize = 0;
    //USBDPutString("VideoDo ENTER");
    if (usbd_video_recording) {
        /* the sample data is only YUY2 160x120 */
#if SUD_VIDEO_IN_FORMAT_UNCOMPRESSED
        if (g_VideoFormat.iFormat == SUD_VIDEO_FORMAT_UNCOMPRESSED && g_VideoFormat.wWidth == 160 && g_VideoFormat.wHeight == 120) {
#if SUD_VIDEO_USE_YUV422
           pVideoData = (u8 *)&pVideoYUV422160x120Data[0];
          iTotalSize = sizeof(pVideoYUV422160x120Data);	
	      //g_VideoFormat.dwMaxPayloadTransferSize = 1024;
#else
#if  MDI_ALL_FORMAT_TEST
            pVideoData = (u8 *)&pVideoYUV420160x120Data[0];
            iTotalSize = sizeof(pVideoYUV420160x120Data);
#else
            USBDPutString("YUV420 data is not available");
#endif
#endif /* SUD_VIDEO_USE_YUV422 */
        } else if (g_VideoFormat.iFormat == SUD_VIDEO_FORMAT_UNCOMPRESSED && g_VideoFormat.wWidth == 320 && g_VideoFormat.wHeight == 240) {
#if SUD_VIDEO_USE_YUV422
           printf("*****************************yuv422---320*240 format****************************\n");
           //g_VideoFormat.dwMaxPayloadTransferSize = 1024;
		   pVideoData = (u8 *)0x2000000;
           iTotalSize = 0x25800;  //yuv422
           
#else
            USBDPutString("YUV422 320*240 data is not available");
#endif
        }
		 else if (g_VideoFormat.iFormat == SUD_VIDEO_FORMAT_UNCOMPRESSED && g_VideoFormat.wWidth == 1280 && g_VideoFormat.wHeight == 720) {
                    USBDPutString("YUV422-1280 720");        
#if SUD_VIDEO_USE_YUV422
					printf("switch mode to IR\n");		////liuhui ir
					vi_set_snap_mode(SNAPSHOT_MODE_DONE);
					vi_set_snap_mode(SNAPSHOT_MODE_UVC_IR_720P_YUV);
					//lfcui
                    //pVideoData = MM_MIPI2DMA_BUF1;  //ir
                    iTotalSize = 1280 *720 *2; //yuv422                       
#else
					USBDPutString("YUV422 1280x720 data is not available");
#endif
				}
		 else if (g_VideoFormat.iFormat == SUD_VIDEO_FORMAT_UNCOMPRESSED && g_VideoFormat.wWidth == 1280 && g_VideoFormat.wHeight == 721) {
                    USBDPutString("YUV422-1280 721");
#if SUD_VIDEO_USE_YUV422
                    printf("switch mode to RGB\n");  	////liuhui rgb
					vi_set_snap_mode(SNAPSHOT_MODE_DONE);
					vi_set_snap_mode(SNAPSHOT_MODE_UVC_RGB_720P_YUV);
                    //lfcui
                    //pVideoData = (u8 *)MM_IMG_AR0230_BUF0;  //rgb
                    iTotalSize = 1280 *(720+1) *2; //yuv422         
#else
					USBDPutString("YUV422 1280x721 data is not available");
#endif
				}
				  else if (g_VideoFormat.iFormat == SUD_VIDEO_FORMAT_UNCOMPRESSED && g_VideoFormat.wWidth == 640 && g_VideoFormat.wHeight == 480) {
#if MDI_ALL_FORMAT_TEST
							printf("*****************************yuv422----640*480 format****************************\n");
							pVideoData = (u8 *)0x2100000;
							iTotalSize = 0x96000; //yuv422
						   // iTotalSize = 0x70800;  //yuv420
							
#else
							 USBDPutString("YUV422 640*480data is not available");
#endif
						 }
						else if (g_VideoFormat.iFormat == SUD_VIDEO_FORMAT_UNCOMPRESSED && g_VideoFormat.wWidth == 640 && g_VideoFormat.wHeight == 360) {
                                   USBDPutString("YUV422-640 360");
#if SUD_VIDEO_USE_YUV422
								   //pVideoData = (u8 *)g_fb_ir_cur;
								//    if(isp1_mi_buf0_ready_flag & isp1_buf_change_flag ) {
                                //    		pVideoData = (u8 *)iva_2ir_get_buffer(1, 0);
								// 		isp1_mi_buf0_ready_flag=  0;
								//    			isp1_buf_change_flag= 0;

								//    }else if(isp1_mi_buf1_ready_flag & isp1_buf_change_flag ) {

								//    pVideoData = (u8 *)iva_2ir_get_buffer(1, 1);
								// 		   isp1_mi_buf1_ready_flag = 0;
								// 		   isp1_buf_change_flag=0;

								//    	}
								  pVideoData = (u8 *)iva_2ir_get_buffer(1, 0); 
								  iTotalSize = g_VideoFormat.wWidth * g_VideoFormat.wHeight * 2; //yuv422		
								  
#else
								   USBDPutString("YUV422 640*360 data is not available");
#endif
							   }

		

#endif /* SUD_VIDEO_IN_FORMAT_UNCOMPRESSED */
#if SUD_VIDEO_IN_FORMAT_MJPEG

        if (g_VideoFormat.iFormat == SUD_VIDEO_FORMAT_MJPEG && g_VideoFormat.wWidth == 160 && g_VideoFormat.wHeight == 120) {
#if MDI_ALL_FORMAT_TEST
            pVideoData = (u8 *)&pVideoMJPEG160x120Data0[0];
            iTotalSize = sizeof(pVideoMJPEG160x120Data0);
#else
            USBDPutString("MJPEG data is not available");
#endif
        }
        if (g_VideoFormat.iFormat == SUD_VIDEO_FORMAT_MJPEG && g_VideoFormat.wWidth == 640 && g_VideoFormat.wHeight == 480) {
				pVideoData = (u8 *)0x6e00000;//0x6e00000;  //daorudizhi;
				iTotalSize = 40000;  //JPEG
		}

#endif /* SUD_VIDEO_IN_FORMAT_MJPEG */

        dwCurrentMSec = sb_OS_MSEC_GET();

        extern int ir_led_power_on();
        ir_led_power_on();
        if (g_sensor_type == 0){
            pVideoData = (u8 *)iva_2ir_get_buffer(0, 0);
            pVideoData[0] = 0x0a;
            sud_VideoSendVideoData(0, pVideoData, iTotalSize);           
        }else if(g_sensor_type == 1){           
            pVideoData = (u8 *)iva_2ir_get_buffer(1, 0);
            pVideoData[0] = 0x0a;
            sud_VideoSendVideoData(0, pVideoData, iTotalSize);         
        }

    }
}
#endif

#endif


/**********************************************************************
 *  USB uvc
 * *******************************************************************/
void usb_uvc_task(void *paras)
{
    printf("usb_uvc_task\r\n");
    while(1)
    {
        VideoDo();
        aos_msleep(33);
    }
}


void usb_uvc_init(void)
{
    int ret;
	vpu_function_init();
    //aos_msleep(3000);
    aos_task_t uvc_thread;
    ret = aos_task_new_ext(&uvc_thread, USB_UVC_MODULE, usb_uvc_task, NULL, 30*1024, USB_UVC_TASK_PRI);
    if (RHINO_SUCCESS != ret) {
        LOGE(USB_UVC_MODULE, "usb uvc task create fail!\n");
        return;
    }
    LOGI(USB_UVC_MODULE, "usb uvc task created!\n");

    return;
}
